#pragma once

#include "Stmt.h"
#include "Expr.h"

#include <vector>
#include <map>
#include <stack>
#include <string>

enum IDType
{
    ID_VAR,
    ID_FUNC,
    ID_ARRAY,
    ID_INVALID
};

struct LeftValueInfo
{
    union
    {
        int value;
        float fValue;
    };
    union
    {
        int maxValue;
        float fMaxValue;
    };
    union
    {
        int step;
        float fStep;
    };
    int nextLine;

    LeftValueInfo ()
        : value(0), maxValue(0), step(1), nextLine(-1)
    {
    }
};

struct VarInfo
{
    std::string id;
    LeftValueInfo content;

    VarInfo(std::string _id)
        :id(_id)
    {
    }
    VarInfo()
        :id("")
    {
    }
};

struct ArrayInfo
{
    std::string id;
    int rSize;
    int lSize;
    std::vector<LeftValueInfo> contents;

    ArrayInfo(std::string _id, int _rSize, int _lSize)
        :id(_id), rSize(_rSize), lSize(_lSize), contents((rSize + 1) * (lSize + 1))
    {        
    }
    ArrayInfo(int _rSize, int _lSize)
        :id(""), rSize(_rSize), lSize(_lSize), contents((rSize + 1) * (lSize + 1))
    {
    }
    ArrayInfo()
        :id(""), rSize(10), lSize(10), contents((rSize + 1) * (lSize + 1))
    {
    }
};

struct FuncInfo
{
    std::string id;
    Expr *body;
    std::map<std::string, VarInfo> localVarMap;
};

class vm
{
public:
    vm(void);
    virtual ~vm(void);
    void Run();
    void AddStmt(Stmt *stmt);
    // About Vars
    int GetVar(std::string id);
    void SetVar(std::string id, int value);
    void SetForInfo(std::string id, int maxValue);
    void SetForStep(std::string id, int step);
    int GetForStep(std::string id);
    int GetMaxValue(std::string id);
    int GetNextLine(std::string id);   
    float GetFltVar(std::string id);
    void SetFltVar(std::string id, float value);
    void SetFltForInfo(std::string id, float maxValue);
    void SetFltForStep(std::string id, float step);
    float GetFltForStep(std::string id);
    float GetFltMaxValue(std::string id);
    // About Arrays
    void AddArray(std::string id, int rSize, int lSize);
    int GetElement(std::string id, int row, int line);
    void SetElement(std::string id, int row, int line, int value);
    void SetElementForInfo(std::string id, int row, int line, int maxValue);
    void SetElementForStep(std::string id, int row, int line, int step);
    int GetElementForStep(std::string id, int row, int line);
    int GetElementMaxValue(std::string id, int row, int line);
    int GetElementNextLine(std::string id, int row, int line);   
    float GetFltElement(std::string id, int row, int line);
    void SetFltElement(std::string id, int row, int line, float value);
    void SetFltElementForInfo(std::string id, int row, int line, float maxValue);
    void SetFltElementForStep(std::string id, int row, int line, float step);
    float GetFltElementForStep(std::string id, int row, int line);
    float GetFltElementMaxValue(std::string id, int row, int line);
    //
    int GetAddrFromLine(int line);
    void PushFunc();
    int PopFunc();
    void AddDataLine(int line);
    void AddData(float data);
    float GetNextFltData();
    void SetDataPos(int line);
    void AddFunction(std::string name, std::string para, Expr *body);
    float EvalFunction(std::string id, float arg);
    IDType GetIdType(std::string id);
    inline bool HasNextData() const
    {
        return dataPos != dataSeg.size();
    }

private:
    std::vector<Stmt *> stmts;
    std::vector<float> dataSeg;
    std::map<int, int> codeLineMap;
    std::map<int, int> dataLineMap;
    std::map<std::string, VarInfo> gobalVarMap;
    std::map<std::string, FuncInfo> funcMap;
    std::map<std::string, ArrayInfo> ArrayMap;
    std::stack<int> funcs;
    int prgCount;
    int dataPos;
    FuncInfo *currFunc;
};

